/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package simulator.engine;

import com.google.gson.Gson;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
import xml.WeatherFinder;



/**
 *
 * @author 490060
 */
public class Simulator 
{
    private WeatherFinder weatherFinder = new WeatherFinder();
    
    
    HeatPump heatPump;
    Inventory inventory;
    Surface surface;
    WaterHeater waterHeater;
    Illumination illumination;
    //Name of xml-file that contains simulated points
    private final String XML_FILE_NAME = "web/Simulated_data.xml";
    private final String COUNTY = "TROMS";
    private final String WEATHER_STATION = "TROMSØ";
    //Inventory 
    private final int INHABITANT = Inventory.LONLEY_PARENT_WITH_ONE_CHILD;
    private final int STRUCTURE_ID = 2;
    
    //waterheater
    private final int CONSUMPTION = 800;
    private final int POPULATION = 3;
    private final int INSIDE_TEMPERATURE = 20;
    
    //Illumination
    private final int NUMBER_OF_LIGHTS = 26;
    private final double KILOWATTS = 0.6;   
    private final float[] USER_PATTERN_FOR_ILLUMINATION = {0.2f, 0.3f, 0.4f, 0.5f, 
        0.6f, 0.7f, 0.8f, 0.9f, 0.1f, 0.2f, 0.3f, 0.4f, 0.6f, 0.7f, 0.8f, 0.9f, 
        1f, 0.4f, 0.5f, 0.6f, 0.7f, 0.6f, 0.7f, 0.9f};
    //other
    private final String START_DATE = "19,11,11,12";
    private final String END_DATE = "20,1,12,13";
    //Vars
    private ArrayList<Surface> walls;
    private double insideTemprature;
    private String startDate;
    private String endDate;
    private String county;
    private String weatherStation;
    private ArrayList<Float> temperatures;
    /**
     * Empty constructor
     */
    public Simulator()
    {
        
    }
    
    public void addWall(double dimX, double dimY, double uValue)
    {
        if(walls == null)
            walls = new ArrayList<Surface>();
        walls.add(new Surface(dimX, dimY, uValue));
    }
    
    //SETTERS
    public void setInsideTemperature(double tempr)
    {
        insideTemprature = tempr;
    }
    
    public void setHeatPump()
    {
        if(walls == null)
            System.out.println("Walls not set!");
        heatPump = new HeatPump(walls);
    }
   
    public void setInventory(int inhabitant, int structureID)
    {
        inventory = new Inventory(inhabitant, structureID);   
    }
    
    public void setWaterHeater(int population, int consumtion)
    {
       waterHeater = new WaterHeater(population, consumtion, (int)insideTemprature, getTemperature(startDate));
    }
    
    public void setIllumination(int numberOfLights, double kilowatts, String lightUsage)
    {
        illumination = new Illumination(numberOfLights, kilowatts);
        illumination.setUserPattern(lightUsage);
    }
    
    public void setIllumination(int numberOfLights, double kilowatts, float[] lightUsage)
    {
        illumination = new Illumination(numberOfLights, kilowatts);
        illumination.setUserPattern(lightUsage);
    }
    
    //GETTERS
    /**
     * Returns an arraylist with all the dates and hours between start and end.
     * 
     * @param startDate D,M,YY,H
     * @param endDate   D,M,YY,H
     * @return 
     */
    public ArrayList<String> getDaysAndHours(String startDate, String endDate)
    {
        int currentMM =Integer.parseInt(startDate.split("-")[1]);
        int currentDD =Integer.parseInt(startDate.split("-")[2]);
        int currentYY = Integer.parseInt(startDate.split("-")[0].substring(2));
        int currentHH = 0;//Integer.parseInt(startDate.split("-")[3]);
        String currentDate = startDate;
        int daysInMonth = getDaysInMonth(currentMM, currentYY);
        ArrayList<String> output = new ArrayList();
        output.add(currentDate);
        try
        {
            while(!currentDate.equals(endDate))
            {
                if(currentHH < 24)
                {
                    //currentDate = currentDD + "-" + currentMM + "-" + currentYY + "-" + currentHH;
                    currentDate = "20"+currentYY+"-"+currentMM+"-"+currentDD;//currentDD + "-" + currentMM + "-" + currentYY + "-" + currentHH;
                    output.add(currentDate);
                    //System.out.println(currentDate);
                    currentHH++;
                }else if(currentDD < daysInMonth)
                {
                    currentDD++;
                    currentHH = 0;
                    //output.add(currentDate);
                    //System.out.println(currentDate);
                }else if(currentMM < 12)
                {
                    currentMM++;
                    currentDD = 1;
                    currentHH = 0;
                    //daysInMonth = getDaysInMonth(currentMM, currentYY);
                    //output.add(currentDate);
                }
                else if(currentYY < 99)
                {
                    currentYY++;
                    currentDD = 1;
                    currentMM = 1;
                    currentHH = 0;
                }
                else
                {
                    currentYY = 0;
                    currentDD = 1;
                    currentMM = 1;
                    currentHH = 0;
                }
            }
        } catch(OutOfMemoryError e)
        {
            System.err.println(e.getMessage());
            System.out.println("Time between start date and end date to long");
        }
        
        return output;
    }
    
    /**
     * 
     * @param datehour D,M,YYYY,H ex: 7,1,1989,23 or 13,11,2002,4
     * @return 
     */
    public int getTemperature(String date)
    {
        /*if(weatherFinder.getTemperatures() == null)
        {
            
            temperatures =  
            weatherFinder.getTemperatures(getDateParsedForTemperature(startDate), 
                    getDateParsedForTemperature(endDate), county, weatherStation, 
                    "0,6,12,18"); 
        }
        int hour = Integer.parseInt(date.split(",")[date.split(",").length]);
        
        if(hour >= 6)
        {
            float()
            
        }*/
        return 0;
            
    }
    
    private String getDateParsedForTemperature(String date)
    {
        String[] d = date.split(",");
        String out = "";
        if(d[2].length() == 1)
            out += "200" + d[0] + "-";
        else// if(d[2].length() == 2)
        {
            if(Integer.parseInt(d[2]) >= 20)
            {
                out += "19" + d[2] + "-";
            }
            else
            {
                out += "20" + d[2] + "-";
            }
        }
        
        if(d[1].length() == 1)
            out += "0" + d[1] + "-";
        else out += d[1] + "-";
        if(d[0].length() == 1)   
            out += "0" + d[0] + "";
        else out += d[0] + "";
        System.out.println("Date: " + out);
                
        return out;
    }
    

    /**
     * 
     * @param year is needed to check if leap year
     * @param month 
     * @return days in month
     */
    private int getDaysInMonth(int month, int year)
    {
        System.out.println("MONTH: " + month);
        if(month == 2 && isLeapYear(year))
        {
            return 29;
        }
        else if(month == 2)
        {
            return 28;
        }
        else if(month % 2 == 0)
        {
            return 30;
        }
        else return 31;
    }
    
    /**
     * returns true if input is a leap year, else returns false
     * @param year
     * @return 
     */
    public float[] getSimulatedHour(ArrayList<String> dateTime)
    {
        System.out.println("Simulation process started");
        float[] energyUsage = new float[dateTime.size()];
        
        for(int i = 0; i < dateTime.size(); i++)
        {
            short hour = '0';//Short.parseShort(dateTime.get(i).split("-")[3]);
            if(inventory != null)
            {
                energyUsage[i] = inventory.getSimulatedHour(hour, true);
            }
            if(waterHeater != null)
            {
                energyUsage[i] += waterHeater.PowerConsumption(getTemperature(dateTime.get(i)));
            }
            if(illumination != null)
            {
                energyUsage[i] += illumination.CalculateUsage(hour);
            }
            if(heatPump != null)
            {
                energyUsage[i] += heatPump.computeWork(insideTemprature, getTemperature(dateTime.get(i)));
            } 
            if(i == dateTime.size() / 4)
                System.out.println("25% COMPLETE");
            else if(i == dateTime.size() / 2)
                System.out.println("50% COMPLETE");
            else if(i == 3 * dateTime.size() / 4)
                System.out.println("75% COMPLETE");
        }
        System.out.println("Simulation finished");
        return energyUsage;
    }
    
    private boolean isLeapYear(int year)
    {
        if(year % 4 == 0)
        {
            if(year % 100 == 0)
            {
                if(year % 400 == 0)
                {
                    return true;
                }
                return false;
            }
            return true;
        }
        return false;
    }
    
    /**
     * 
     * @param simulatedHours Arraylist with simulated float values
     * @param maxJump highest allowed jump in energyuage
     * @return 
     */
    private float[] evenOutSimulatedHours(float[] toEvenOut, float maxJump)
    {
        float min = 100 - maxJump;
        float max = 100 + maxJump;
        
        float[] out = new float[toEvenOut.length];
        out[0] = toEvenOut[0];
        for(int i = 1; i < toEvenOut.length; i++)
        {
            float currJump = toEvenOut[i] / out[i-1] * 100;
//            System.out.println("array: " + toEvenOut[i] +"currJump: " + currJump);
            if(currJump > max || currJump < min)
            {
                if(currJump > max)
                    out[i] = out[i-1] * max / 100;
                else
                    out[i] = out[i-1] * min / 100;
            } else
            out[i] = toEvenOut[i];
        }
        
        return out;
    }
    
    private String writeToXML(String startDate, float[] simulated) throws Exception
    {
        long time = getTimeConvertedToUTC(startDate);
        
        FileWriter fw = new FileWriter(XML_FILE_NAME);
        String xml = "";
        xml += ("<chart>\n<series>\n<date>" + time + "</date>\n<data>");
        for(float sim : simulated)
            xml += "\n<point>" + sim + "</point>";
        
        xml += "</data>\n</series></chart>";
        fw.write(xml);
        fw.close();
        return xml;
    }

    private long getTimeConvertedToUTC(String time)
    {
        SimpleDateFormat df = new SimpleDateFormat("d,M,yy,h");
        Date date = null;
        try
        {
            date = df.parse(time);
        }catch(Exception e)
        {
            e.printStackTrace();
        }
        
        return date.getTime();
    }
    /**
     * Simulated array is registered as "Sim".
     * @param startDate
     * @param simulated
     * @return 
     */
    public JsonObject getSimulatedArrayInJson(String startDate, float[] simulated)
    {
        Gson out = new Gson();
        JsonObject object = new JsonObject();
        String sim = "";
        try 
        {
            sim = writeToXML(startDate, simulated);
            object.addProperty("Sim", sim);
        } catch (Exception ex)
        {
            Logger.getLogger(Simulator.class.getName()).log(Level.SEVERE, null, ex);
        }
        
        return object;
    }
    
    /**
     * Siden jeg er blitt bitcha til "Jan Tore den Ubeslutsomme" skriver jeg 
     * denne metoden. Den gjør det samme som metoden over bare bedre. 
     * @param args 
     */
    public JsonObject getSuperFineJSonObject(String startDate, float[] simulated)
    {
        JsonObject jo = new JsonObject();
        jo.addProperty("StartDate", getTimeConvertedToUTC(startDate));
        for(int i = 0; i < simulated.length; i++)
            jo.addProperty("sim" + i, simulated[i]);
        return jo;
    }
    
    public static void main(String[] args)
    {
        Simulator s = new Simulator();
        
        /*s.addWall(8000, 2000, 0.46);
        s.addWall(6000, 2000, 0.46);
        s.addWall(8000, 2000, 0.46);
        s.addWall(6000, 2000, 0.46);
        s.setInsideTemperature(9);
        
        s.setHeatPump();
        s.setInventory(s.INHABITANT, s.STRUCTURE_ID);
        s.setWaterHeater(s.POPULATION, s.CONSUMPTION);
        s.setIllumination(s.NUMBER_OF_LIGHTS, s.KILOWATTS, s.USER_PATTERN_FOR_ILLUMINATION);
        
        s.getTimeConvertedToUTC(END_DATE);

        ArrayList<Float> tst = new ArrayList<Float>();
        ArrayList<String> time = s.getDaysAndHours(START_DATE, END_DATE);
        float[] simulatedUsage = s.getSimulatedHour(time);
        for(float f:simulatedUsage)
            tst.add(f);
        JsonObject json;
        Gson jayson = new Gson();
        float[] per = s.evenOutSimulatedHours(simulatedUsage, 30);
        try
        {
            json = s.getSimulatedArrayInJson(START_DATE, per);
            System.out.println("JSON: " + jayson.fromJson(json.get("Sim"), String.class));
        }catch(Exception e)
        {
            System.out.println("Did not write XML");
            e.printStackTrace();
        }
//        for(int i = 0; i < simulatedUsage.length; i++)
//            System.out.println("Simulert: "  + tst.get(i) + "\tPercent: " + per[i]);*/
        float[][] walls = new float[4][3];
        for(int i = 0; i < walls.length; i++)
        {
            walls[i][0] = 6000f;
            walls[i][1] = 4000f;
            walls[i][2] = 0.46f;
        }
        JsonObject json = s.simulate(s.INHABITANT, s.STRUCTURE_ID, s.CONSUMPTION, s.POPULATION, 
                s.INSIDE_TEMPERATURE, s.NUMBER_OF_LIGHTS, s.KILOWATTS, s.USER_PATTERN_FOR_ILLUMINATION, 
                s.START_DATE, s.END_DATE, walls, 40, s.COUNTY, s.WEATHER_STATION);
        Gson gson = new Gson();
        System.out.println("JSON: " + json);
        s.getDateParsedForTemperature(s.START_DATE);
    }
    
    /**
     * Runs the whole simulation process with the following parameters
     * @param inhabitantID Inventory.java ID witch represents the family in DB
     * @param structure_ID Inventory.java ID witch represents the building in DB
     * @param consumption WaterHeater.java the average water usage for one inhabitant
     * @param population WaterHeater.java number of people living in the house 
     * @param inside_temperature the inhabitants comfort temperature
     * @param numberOfLights Illumination.java number of lights in the house
     * @param kilowatts Illumination.java average energy usage for each light 
     * @param userPatternForIlumination array witch represents the percentage of 
     * lights in use at a given time of day. Each unit represents one hour of the day
     * @param startDate d,m,yy,h
     * @param endDate d,m,yy,h
     * @param walls array that contains one array for each wall. Each of those arrays 
     * represents one parameter for each parameters needed to create a wall
     * param-0: X dimension of the wall
     * param-1: y dimension of the wall
     * param-2: the uvalue of the wall
     * example:
     * |6000|8000|0.46|_|6000|8000|0.46|_|3000|8000|0.47|_|3000|8000|0.47|
     * @param maxJump the highest allowed peak in simulation output. percentage 
     * @return JsonObject for use in graph display.
     */
    public JsonObject simulate(int inhabitantID, int structure_ID, 
            int consumption, int population, int inside_temperature, 
            int numberOfLights, double kilowatts, float[] userPatternForIlumination,
            String startDate, String endDate, float[][] walls, int maxJump, String county, String WeatherStationName)
    {
        for(int i = 0; i < walls.length; i++)
            addWall(walls[i][0], walls[i][1], walls[i][2]);
        
        this.startDate = startDate;
        this.endDate = endDate;
        this.county = county;
        this.weatherStation = WeatherStationName;
        setHeatPump();
        setInventory(inhabitantID, structure_ID);
        setWaterHeater(population, consumption);
        setIllumination(numberOfLights, kilowatts, userPatternForIlumination);
        setInsideTemperature(inside_temperature);

        
        return getSuperFineJSonObject(startDate, 
                evenOutSimulatedHours(
                getSimulatedHour(
                getDaysAndHours(startDate, endDate)), maxJump));
    }
    

    
    //    private String[] evenOutSimulatedHours(float[] simulatedHours, int maxJump)
//    {
//        
//        
//        System.out.println("Evening out");
//        Float[] evenedOut = new Float[simulatedHours.length];
//        evenedOut[0] = simulatedHours[0];
//        String[] output = new String[evenedOut.length];
//        float currentJumpInPercentage;
//        for(int i = 1; i < simulatedHours.length; i++)
//        {
//            /* Bug fixed by doing this if-test*/
////            if(i == 1)
//                currentJumpInPercentage = simulatedHours[i-1] / simulatedHours[i] * 100;
////            else
////                currentJumpInPercentage = evenedOut[i-1] / simulatedHours[i] * 100;
//            
//            boolean lessThan = currentJumpInPercentage < (100 + maxJump);
//            boolean moreThan = currentJumpInPercentage > (100 - maxJump);
//
//            if (!moreThan && lessThan)
//            {
////                System.out.print("Justeres ned fra " + simulatedHours[i]);
//                evenedOut[i] =evenedOut[i-1] + (maxJump / evenedOut[i-1] * 100);
////                System.out.println("til " + evenedOut[i]);
//            }
//            else if (!lessThan && moreThan)
//            {
////                System.out.print("Justeres opp fra " + simulatedHours[i]);
//                evenedOut[i] = evenedOut[i-1] - (maxJump / evenedOut[i-1] * 100); 
////                System.out.println("til " + evenedOut[i]);
//            } else
////            if(moreThan && lessThan)
//            {
////              System.out.println("No change");
//                boolean evenedOutLessThan = evenedOut[i-1] / simulatedHours[i] * 100 < 100 + maxJump;
//                boolean evenedOutMoreThan = evenedOut[i-1] / simulatedHours[i] * 100 > 100 - maxJump;
//
//                if(evenedOutLessThan && !evenedOutMoreThan)
//                    evenedOut[i] = evenedOut[i-1] + maxJump / evenedOut[i-1] * 100;
//                else if(!evenedOutLessThan && evenedOutMoreThan)
//                {
//                    evenedOut[i] = evenedOut[i-1] - (maxJump / evenedOut[i-1] * 100); 
//                    output[i] = "Percentage: " + currentJumpInPercentage + "\tEvenedOut: " + evenedOut[i-1] / simulatedHours[i] * 100;
//                }
//                else//(evenedOutLessThan && evenedOutMoreThan)
//                {
//                    evenedOut[i] = simulatedHours[i];
////                    output[i] = "Percentage: " + currentJumpInPercentage;
//                }
//            }
//        }
//        
//        
//        for(int i = 0; i < output.length; i++)
//        {
//            if(output[i] != null)
//                output[i] = evenedOut[i] + " :\t" + output[i]; 
//            else output[i] = evenedOut[i] + "";
//            
//        }
//        return output;
//        
//    }
}
